<?php
namespace Tlf\Scrawl;
/**
* Bumps versions. This implementation may be moved to its own, separate library. Idk.
*
*/
class VersionBumper {
/**
* Use system git commands to determine current version from the branch name and available tags.
*
* You should run `git fetch` before using this for accurate results.
*
* @param $bump_rules array<int index_in_version_string, array string_prefixes> string prefixes are literal, except a single asterisk (`*`) will always bump that version number, and should only be present if you want a default case of bumping the last index
*
* @return string version like 0.8.0.1 from 0.8.0.0
*/
public function get_new_version_from_git(array $bump_rules): string {
$last_commit = exec("git log -1 --pretty=%B");
$branch = $this->get_current_branch();
$branch_version = $this->get_branch_version($branch);
$latest_tag = $this->get_latest_tag($branch_version);
$new_version = $this->get_new_version($latest_tag, $last_commit, $bump_rules);
return $new_version;
}
/**
* Get a bumped version number. Returns `$latest_version` if none of the `$bump_rules` apply.
*
* Ex: 0.8.0 gets commit 'bugfix: something', and bumps to 0.8.0.1 based on the bump rules provided.
* Ex: 0.8.0.2 gets commit 'feature: cool stuff', and bumps to 0.8.1.0 based on the bump rules provided.
* Ex: 0.8.0 gets commit 'notes', and does not bump, so '0.8.0' is returned.
*
* @param $latest_version string of numbers separated by periods, like '3.8.1'
* @param $latest_commit_msg string with a prefix that indicates which portion of the version string to bump (increase)
* @param $bump_rules array<int index_in_version_string, array string_prefixes> string prefixes are literal, except a single asterisk (`*`) will always bump that version number, and should only be present if you want a default case of bumping the last index
*
* @return string representing a new version number. This may be the SAME as $latest_version if no $bump_rules passed.
*/
public function get_new_version(string $latest_version, string $latest_commit_msg, array $bump_rules): string {
$index_to_bump = $this->get_index_to_bump($latest_commit_msg, $bump_rules);
$i = $index_to_bump;
$parts = explode(".", $latest_version);
$length = count($parts);
while (($length = count($parts)) <= $index_to_bump){
$parts[] = '0';
}
if ($i>=0){
$parts[$i] = ((int)$parts[$i])+1;
while (++$i < $length){
$parts[$i] = 0;
}
}
$new_version = implode(".", $parts);
return $new_version;
}
/**
* @return int index to increase by 1, or -1 if there should not be a version increase.
*/
public function get_index_to_bump(string $commit_message, array $index_rules): int{
foreach ($index_rules as $index_to_bump => $prefixes_to_cause_bump){
foreach ($prefixes_to_cause_bump as $prefix){
if ($prefix=='*')return $index_to_bump;
$len = strlen($prefix);
if (substr($commit_message,0,$len+1) == "$prefix:"){
return $index_to_bump;
}
}
}
return -1;
}
/**
*
* @param $version_prefix string like "1.0" or "0.8"
*/
public function get_latest_tag(string $version_prefix=''): string{
ob_start();
system("git tag --list \"$version_prefix.*\"");
$available_versions = ob_get_clean();
$tags = explode("\n", trim($available_versions));
//echo implode(" ", $tags);
$max_bug_version = -1;
foreach ($tags as $full_version){
if (trim($full_version)=='')continue;
$parts = explode('.',$full_version);
$bugversion = (int)$parts[2];
if ($bugversion > $max_bug_version) $max_bug_version = $bugversion;
}
return trim("$version_prefix.$max_bug_version");
}
/**
* Return the name of the current git branch
*
*/
public function get_current_branch(): string {
return exec("git rev-parse --abbrev-ref HEAD");
}
/**
* Assumes branch name starts with 'v', removes it, and returns the remaining string, assumed to be numbers separated by periods.
*
* @return string like 1.0
*/
public function get_branch_version(string $branch_name): string {
return substr($branch_name,1);
}
}